package com.ponhu.ea2.goodsCenter.provider.schedule;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ponhu.ea2.entity.*;
import com.ponhu.ea2.enums.ApplyCrowdEnum;
import com.ponhu.ea2.enums.SyncTaskTypeEnum;
import com.ponhu.ea2.goodsCenter.provider.service.*;
import com.ponhu.ea2.goodsCenter.provider.utils.CommonUtils;
import com.ponhu.ea2.query.GoodsDataSyncQuery;
import com.ponhu.ea2.vo.GoodsDataAttributeSyncVo;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 胖总管商品属性数据同步任务
 *
 * @author zhaokai
 */
@Slf4j
@Component
public class GoodsInitSyncTask {

    @Resource
    GoodsService goodsService;
    @Resource
    MaterialService materialService;
    @Resource
    PhSeriesService phSeriesService;
    @Resource
    PhBrandService phBrandService;
    @Resource
    InventoryService inventoryService;
    @Resource
    PonHuGoodsDataService ponHuGoodsDataService;
    @Resource
    GoodsInitDataSyncService goodsInitDataSyncService;
    @Resource
    GoodsDataSyncTaskLogService goodsDataSyncTaskLogService;

    @XxlJob("goodsInitSyncTask")
    public ReturnT<String> goodsAttributeDataTask(String param) {
        log.info("胖总管商品属性数据初始化任务执行开始,{}", LocalDateTime.now());
        // 取出最后一次执行任务的最新时间
        Date lastDateEditTime = goodsDataSyncTaskLogService.getLastDataEditTime(SyncTaskTypeEnum.ATTRIBUTE.getCode());
        // 保存任务执行日志
        GoodsDataSyncTaskLog goodsDataSyncTaskLog = new GoodsDataSyncTaskLog();
        goodsDataSyncTaskLog.setStartTime(LocalDateTime.now());
        GoodsDataSyncQuery goodsDataSyncQuery = new GoodsDataSyncQuery();
        // 胖总管货品数据
        List<GoodsDataAttributeSyncVo> goodsDataAttributeSyncVos = new ArrayList<>();
        // 查询胖总管数据前先查一下本地是否有数据，如有则表示已经同步过数据，则只取指定时间内，否则取全量数据
        Integer count = inventoryService.count();
        log.info("商品中心属性数据，货品数据已有{}条", count);
        if (count > 0 && null != lastDateEditTime) {
            goodsDataSyncQuery.setDataEditTime(lastDateEditTime);
            goodsDataAttributeSyncVos = ponHuGoodsDataService.selectPhGoodsAttributeData(goodsDataSyncQuery);
        } else {
            // 查询胖总管数据，这里第一次查询出来的数据可能会很大
            goodsDataAttributeSyncVos = ponHuGoodsDataService.selectPhGoodsAttributeData(goodsDataSyncQuery);
        }
        log.info("商品中心属性数据，取出胖总管数据{}条", goodsDataAttributeSyncVos.size());
        if (!CollectionUtils.isEmpty(goodsDataAttributeSyncVos)) {
            // 保存日志的最新更新时间和货品编码值
            goodsDataSyncTaskLog.setDataEditTime(goodsDataAttributeSyncVos.get(0).getDataEditTime());
            goodsDataSyncTaskLog.setCode(goodsDataAttributeSyncVos.get(0).getCode());
            goodsInitDataSyncService.goodsAttributeDataSync(goodsDataAttributeSyncVos);
        }
        log.info("胖总管商品属性数据初始化任务执行结束,{}", LocalDateTime.now());
        // 任务结束时间
        goodsDataSyncTaskLog.setEndTime(LocalDateTime.now());
        Map<String, Object> remarksMap = new HashMap<>();
        remarksMap.put("取出胖总管条数", goodsDataAttributeSyncVos.size());
        goodsDataSyncTaskLog.setRemarks(JSON.toJSONString(remarksMap));
        goodsDataSyncTaskLog.setTaskType(SyncTaskTypeEnum.ATTRIBUTE.getCode());
        goodsDataSyncTaskLogService.save(goodsDataSyncTaskLog);
        return ReturnT.SUCCESS;
    }

    /**
     * 商品中心数据初始化
     * 入参示例：{"threadBatch": 8, "allOrPart1": false, "allOrPart2": false}
     */
    @XxlJob("goodsInitSyncTask2")
    public void goodsInitSyncTask2() {
        long start = System.currentTimeMillis();
        log.info("XXL-Job ===>> 商品中心数据初始化 ===>> 开始 ===>> currentTimeMillis = {}", start);

        int threadBatch = 10;
        boolean allOrPart1 = true; // true-All，false-Part
        boolean allOrPart2 = true; // true-All，false-Part
        String param = XxlJobHelper.getJobParam();
        if (StringUtils.isNotBlank(param)) {
            XxlJobHelper.log("商品中心数据初始化 ===>> param = {} ===>> {}",
                    param, Thread.currentThread().getName());
            log.info("XXL-Job ===>> 商品中心数据初始化 ===>> param = {}", param);
            try {
                JSONObject jsonObject = JSON.parseObject(param);
                threadBatch = jsonObject.getIntValue("threadBatch");
                allOrPart1 = jsonObject.getBoolean("allOrPart1");
                allOrPart2 = jsonObject.getBoolean("allOrPart2");
            } catch (Exception e) {
                e.printStackTrace();
                XxlJobHelper.handleFail("商品中心数据初始化 ===>> 入参解释异常");
                log.info("XXL-Job ===>> 商品中心数据初始化 ===>> 入参解释异常");
                return;
            }
        }

        int finalThreadBatch = Math.max(1, threadBatch);
        boolean finalAllOrPart1 = allOrPart1; // true-All，false-Part
        // 初始化物料的编码和名称
        CompletableFuture<Void> materialFuture = CompletableFuture.runAsync(() -> {

            LambdaQueryWrapper<Material> queryWrapper = Wrappers
                    .lambdaQuery(Material.class)
                    .eq(Material::getMaterialSource, 4);
            if (BooleanUtils.isFalse(finalAllOrPart1)) queryWrapper.nested(qw ->
                    qw.isNull(Material::getMaterialCode).or().eq(Material::getMaterialCode, ""));
            List<Material> materialList = materialService.list(queryWrapper);

            CompletableFuture<Map<Long, PhBrand>> brandFuture = CompletableFuture.supplyAsync(() -> {
                List<Long> brandIds = materialList.parallelStream().map(Material::getBrandId).collect(Collectors.toList());
                if (com.alibaba.nacos.common.utils.CollectionUtils.isNotEmpty(brandIds))
                    return phBrandService.listByIds(brandIds)
                            .parallelStream().collect(Collectors.toMap(PhBrand::getId, Function.identity()));
                return Maps.newHashMap();
            });

            CompletableFuture<Map<Long, PhSeries>> seriesFuture = CompletableFuture.supplyAsync(() -> {
                List<Long> seriesIds = materialList.parallelStream().map(Material::getSeriesId).collect(Collectors.toList());
                if (com.alibaba.nacos.common.utils.CollectionUtils.isNotEmpty(seriesIds))
                    return phSeriesService.listByIds(seriesIds)
                            .parallelStream().collect(Collectors.toMap(PhSeries::getId, Function.identity()));
                return Maps.newHashMap();
            });

            brandFuture.thenAcceptBoth(seriesFuture, (brandMap, seriesMap) -> {

                ConcurrentHashMap<String, AtomicInteger> hashMap = new ConcurrentHashMap<>();
                materialList.parallelStream().forEach(material -> {
                    long thirdCategoryId = material.getCategoryId() == null ? 0 : material.getCategoryId();
                    String materialCode = CommonUtils.createAndGetMaterialCode(thirdCategoryId, hashMap);
                    PhBrand phBrand = brandMap.get(material.getBrandId());
                    PhSeries phSeries = seriesMap.get(material.getSeriesId());
                    material.setMaterialCode(materialCode).setMaterialName(processName2(material, phBrand, phSeries));
                });

                int size = materialList.size() / finalThreadBatch;

                int index = 0;
                List<CompletableFuture> futureList = Lists.newArrayList();
                for (int i = 0; i < materialList.size(); i++) {
                    if (i % size == 0) {
                        int finalIndex = index++;
                        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                            List<Material> batchMaterialList = materialList.stream()
                                    .skip(finalIndex * size).limit(size).collect(Collectors.toList());
                            if (CollectionUtils.isNotEmpty(batchMaterialList)) {
                                boolean updateBatchById = materialService.updateBatchById(batchMaterialList, Math.min(10000, batchMaterialList.size()));
//                                XxlJobHelper.log("分批初始化物料的编码和名称 ===>> index = {} ===>> {} ===>> {}",
//                                        finalIndex, updateBatchById, Thread.currentThread().getName());
                                log.info("分批初始化物料的编码和名称 ===>> index = {} ===>> {} ===>> {}",
                                        finalIndex, updateBatchById, Thread.currentThread().getName());
                            }
                        });
                        if (ObjectUtils.isNotEmpty(future)) futureList.add(future);
                    }
                }

                if (CollectionUtils.isNotEmpty(futureList)) {
                    CompletableFuture[] futureArr = new CompletableFuture[futureList.size()];
                    CompletableFuture.allOf(futureList.toArray(futureArr)).join();
                }
            }).exceptionally(e -> {
                e.printStackTrace();
//                XxlJobHelper.log("初始化物料的编码和名称 ===>> 异常 ===>> Error={} ===>> detailCauseMsg={}",
//                        e.getMessage(), CommonUtils.getDetailCauseMsg(e));
                log.error("初始化物料的编码和名称 ===>> 异常 ===>> Error={} ===>> detailCauseMsg={}",
                        e.getMessage(), CommonUtils.getDetailCauseMsg(e));
                return null;
            }).join();
        });

        // 初始化商品的编码
        boolean finalAllOrPart2 = allOrPart2; // true-All，false-Part
        CompletableFuture<Void> goodFuture = CompletableFuture.runAsync(() -> {
            try {
                LambdaQueryWrapper<Goods> queryWrapper = Wrappers
                        .lambdaQuery(Goods.class).eq(Goods::getType, 2);
                if (BooleanUtils.isFalse(finalAllOrPart2)) queryWrapper.isNull(Goods::getGoodsCode);
                List<Goods> goodsList = goodsService.list(queryWrapper).parallelStream().map(goods ->
                                goods.setGoodsCode(CommonUtils.createGoodsCode()))
                        .collect(Collectors.toList());

                if (CollectionUtils.isNotEmpty(goodsList)) {
                    boolean updateBatchById = goodsService.updateBatchById(goodsList, Math.min(10000, goodsList.size()));
//                    XxlJobHelper.log("初始化商品的编码 ===>> {} ===>> {}",
//                            updateBatchById, Thread.currentThread().getName());
                    log.info("初始化商品的编码 ===>> {} ===>> {}",
                            updateBatchById, Thread.currentThread().getName());
                }
            } catch (Exception e) {
                e.printStackTrace();
//                XxlJobHelper.log("初始化商品的编码 ===>> 异常 ===>> Error={} ===>> detailCauseMsg={}",
//                        e.getMessage(), CommonUtils.getDetailCauseMsg(e));
                log.error("初始化商品的编码 ===>> 异常 ===>> Error={} ===>> detailCauseMsg={}",
                        e.getMessage(), CommonUtils.getDetailCauseMsg(e));
            }
        });

        CompletableFuture.runAsync(() -> {
            CompletableFuture.allOf(materialFuture, goodFuture).thenRun(() -> {
                log.info("XXL-Job ===>> 商品中心数据初始化 ===>> 成功 ===>> 耗时：{} ms", System.currentTimeMillis() - start);
            });
        });

        XxlJobHelper.handleSuccess("商品中心数据初始化 ===>> 成功启动");
    }

    private String processName2(Material item, PhBrand brand, PhSeries series) {
        StringBuffer stringBuffer = new StringBuffer();

        if (item.getBrandId() != null && ObjectUtils.isNotEmpty(brand)) {
            if (StringUtils.isNotBlank(brand.getEnName())) {
                stringBuffer.append(brand.getEnName());
            }
            if (StringUtils.isNotBlank(brand.getCnName())) {
                stringBuffer.append("/");
                stringBuffer.append(brand.getCnName());
            }
        }

        if (item.getSeriesId() != null && ObjectUtils.isNotEmpty(series)) {
            stringBuffer.append(" ");
            stringBuffer.append(series.getName());
        }

        if (item.getApplyCrowd() != null) {
            stringBuffer.append(" ");
            stringBuffer.append(ApplyCrowdEnum.getName(item.getApplyCrowd()));
        }

        return stringBuffer.toString();
    }

}